home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / papertile.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-02-13  |  27.0 KB  |  957 lines

  1. /*============================================================================*
  2.  
  3.   Paper Tile 1.0  -- A GIMP PLUG-IN
  4.  
  5.   Copyright (C) 1997-1999 Hirotsuna Mizuno <s1041150@u-aizu.ac.jp>
  6.  
  7.   This program  is  free software;  you can redistribute it  and/or  modify it
  8.   under the terms of the GNU Public License as published  by the Free Software
  9.   Foundation;  either version 2 of the License,  or (at your option) any later
  10.   version.
  11.  
  12.   This program is distributed in the hope that it will be useful,  but WITHOUT
  13.   ANY WARRANTY;  without  even  the  implied  warranty  of MERCHANTABILITY  or
  14.   FITNESS  FOR  A PARTICULAR PURPOSE.  See the GNU General Public License  for
  15.   more details.
  16.  
  17.   You should have received a copy of the GNU General Public License along with
  18.   this program; if not, write to the Free Software Foundation, Inc.,
  19.   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  
  21.  *============================================================================*/
  22.  
  23. #include "config.h"
  24.  
  25. #include <stdlib.h>
  26.  
  27. #include <gtk/gtk.h>
  28.  
  29. #include <libgimp/gimp.h>
  30. #include <libgimp/gimpui.h>
  31.  
  32. #include "libgimp/stdplugins-intl.h"
  33.  
  34. /*============================================================================*/
  35. /* DEFINES                                                                    */
  36. /*============================================================================*/
  37.  
  38. #define PLUGIN_PROCEDURE_NAME     "plug_in_papertile"
  39.  
  40. /*============================================================================*/
  41. /* TYPES                                                                      */
  42. /*============================================================================*/
  43.  
  44. typedef enum
  45. {
  46.   BACKGROUND_TYPE_TRANSPARENT,
  47.   BACKGROUND_TYPE_INVERTED,
  48.   BACKGROUND_TYPE_IMAGE,
  49.   BACKGROUND_TYPE_FOREGROUND,
  50.   BACKGROUND_TYPE_BACKGROUND,
  51.   BACKGROUND_TYPE_COLOR
  52. } BackgroundType;
  53.  
  54. typedef enum
  55. {
  56.   FRACTIONAL_TYPE_BACKGROUND,           /* AS BACKGROUND */
  57.   FRACTIONAL_TYPE_IGNORE,               /* NOT OPERATED  */
  58.   FRACTIONAL_TYPE_FORCE                 /* FORCE DIVISION */
  59. } FractionalType;
  60.  
  61. typedef struct _PluginParams PluginParams;
  62. struct _PluginParams
  63. {
  64.   gint32          tile_size;
  65.   gint32          division_x;
  66.   gint32          division_y;
  67.   gdouble         move_max_rate;
  68.   FractionalType  fractional_type;
  69.   gboolean        centering;
  70.   gboolean        wrap_around;
  71.   BackgroundType  background_type;
  72.   guchar          background_color[4];
  73. };
  74.  
  75. /*============================================================================*/
  76. /* VARIABLES                                                                  */
  77. /*============================================================================*/
  78.  
  79. static struct
  80. {
  81.   PluginParams  params;
  82.  
  83.   gint32        image;
  84.   GimpDrawable    *drawable;
  85.   gboolean      drawable_has_alpha;
  86.   
  87.   struct
  88.   {
  89.     gint        x0;
  90.     gint        y0;
  91.     gint        x1;
  92.     gint        y1;
  93.     gint        width;
  94.     gint        height;
  95.   } selection;
  96.   
  97.   GimpRunModeType  run_mode;
  98.   gboolean      run;
  99. } p =
  100. {
  101.   {
  102.     1,                          /* tile_size             */
  103.     16,                         /* division_x            */
  104.     16,                         /* division_y            */
  105.     25.0,                       /* move_max_rate         */
  106.     FRACTIONAL_TYPE_BACKGROUND, /* fractional_type       */
  107.     TRUE,                       /* centering             */
  108.     FALSE,                      /* wrap_around           */
  109.     BACKGROUND_TYPE_INVERTED,   /* background_type       */
  110.     { 0, 0, 255, 255 }          /* background_color[4]   */
  111.   },
  112.  
  113.   0,                            /* image                 */
  114.   NULL,                         /* drawable              */
  115.   FALSE,                        /* drawable_has_alpha    */
  116.  
  117.   { 0, 0, 0, 0, 0, 0 },         /* selection             */
  118.   
  119.   GIMP_RUN_INTERACTIVE,              /* run_mode              */
  120.   FALSE                         /* run                   */
  121. };
  122.  
  123. /*----------------------------------------------------------------------------*/
  124.  
  125. static void
  126. params_save_to_gimp (void)
  127. {
  128.   gimp_set_data (PLUGIN_PROCEDURE_NAME, &p.params, sizeof p.params);
  129. }
  130.  
  131. static void
  132. params_load_from_gimp (void)
  133. {
  134.   gimp_get_data (PLUGIN_PROCEDURE_NAME, &p.params);
  135.  
  136.   if (0 < p.params.division_x)
  137.     {
  138.       p.params.tile_size  = p.drawable->width / p.params.division_x;
  139.       if (0 < p.params.tile_size)
  140.     {
  141.       p.params.division_y = p.drawable->height / p.params.tile_size;
  142.     }
  143.     }
  144.   if (p.params.tile_size <= 0 ||
  145.       p.params.division_x <= 0 ||
  146.       p.params.division_y <= 0)
  147.     {
  148.       p.params.tile_size  = MIN (p.drawable->width, p.drawable->height);
  149.       p.params.division_x = p.drawable->width / p.params.tile_size;
  150.       p.params.division_y = p.drawable->height / p.params.tile_size;
  151.     }
  152.   if (!p.drawable_has_alpha)
  153.     {
  154.       if (p.params.background_type == BACKGROUND_TYPE_TRANSPARENT)
  155.     {
  156.       p.params.background_type = BACKGROUND_TYPE_INVERTED;
  157.     }
  158.       p.params.background_color[3] = 255;
  159.     }
  160. }
  161.  
  162. /*============================================================================*/
  163. /* GUI                                                                        */
  164. /*============================================================================*/
  165.  
  166. static struct
  167. {
  168.   GtkObject *tile_size_adj;
  169.   GtkObject *division_x_adj;
  170.   GtkObject *division_y_adj;
  171. } w;
  172.  
  173. static void
  174. tile_size_adj_changed (GtkAdjustment *adj)
  175. {
  176.   if (p.params.tile_size != (gint)adj->value)
  177.     {
  178.       p.params.tile_size  = adj->value;
  179.       p.params.division_x = p.drawable->width  / p.params.tile_size;
  180.       p.params.division_y = p.drawable->height / p.params.tile_size;
  181.       gtk_adjustment_set_value (GTK_ADJUSTMENT (w.division_x_adj),
  182.                 p.params.division_x);
  183.       gtk_adjustment_set_value (GTK_ADJUSTMENT (w.division_y_adj),
  184.                 p.params.division_y);
  185.   }
  186. }
  187.  
  188. static void
  189. division_x_adj_changed (GtkAdjustment *adj)
  190. {
  191.   if (p.params.division_x != (gint)adj->value)
  192.     {
  193.       p.params.division_x = adj->value;
  194.       p.params.tile_size  = p.drawable->width  / p.params.division_x;
  195.       p.params.division_y =
  196.     p.drawable->height * p.params.division_x / p.drawable->width;
  197.       gtk_adjustment_set_value (GTK_ADJUSTMENT (w.tile_size_adj),
  198.                 p.params.tile_size);
  199.       gtk_adjustment_set_value (GTK_ADJUSTMENT (w.division_y_adj),
  200.                 p.params.division_y);
  201.   }
  202. }
  203.  
  204. static void
  205. division_y_adj_changed (GtkAdjustment *adj)
  206. {
  207.   if (p.params.division_y != (gint)adj->value)
  208.     {
  209.       p.params.division_y = adj->value;
  210.       p.params.tile_size  = p.drawable->height / p.params.division_y;
  211.       p.params.division_x =
  212.     p.drawable->width * p.params.division_y / p.drawable->height;
  213.       gtk_adjustment_set_value (GTK_ADJUSTMENT (w.tile_size_adj),
  214.                 p.params.tile_size);
  215.       gtk_adjustment_set_value (GTK_ADJUSTMENT (w.division_x_adj),
  216.                 p.params.division_x);
  217.   }
  218. }
  219.  
  220. static void
  221. dialog_ok_clicked (GtkWidget *widget,
  222.            gpointer   data)
  223. {
  224.   p.run = TRUE;
  225.  
  226.   gtk_widget_destroy (GTK_WIDGET (data));
  227. }
  228.  
  229. static void
  230. open_dialog (void)
  231. {
  232.   GtkWidget *dialog;
  233.   GtkWidget *main_hbox;
  234.   GtkWidget *button;
  235.   GtkObject *adjustment;
  236.   GtkWidget *vbox;
  237.   GtkWidget *table;
  238.   GtkWidget *frame;
  239.   GtkWidget *box;
  240.   GtkWidget *color_button;
  241.   GtkWidget *sep;
  242.  
  243.   gimp_ui_init ("papertile", TRUE);
  244.  
  245.   dialog = gimp_dialog_new (_("Paper Tile"), "papertile",
  246.                 gimp_standard_help_func, "filters/papertile.html",
  247.                 GTK_WIN_POS_MOUSE,
  248.                 FALSE, FALSE, FALSE,
  249.  
  250.                 _("OK"), dialog_ok_clicked,
  251.                 NULL, NULL, NULL, TRUE, FALSE,
  252.                 _("Cancel"), gtk_widget_destroy,
  253.                 NULL, 1, NULL, FALSE, TRUE,
  254.  
  255.                 NULL);
  256.  
  257.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  258.               GTK_SIGNAL_FUNC (gtk_main_quit),
  259.               NULL);
  260.  
  261.   main_hbox = gtk_hbox_new (FALSE, 5);
  262.   gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 6);
  263.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_hbox);
  264.   gtk_widget_show (main_hbox);
  265.  
  266.   /* Left */
  267.   vbox = gtk_vbox_new (FALSE, 4);
  268.   gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
  269.   gtk_widget_show (vbox);
  270.  
  271.   frame = gtk_frame_new (_("Division"));
  272.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  273.   gtk_widget_show (frame);
  274.  
  275.   table = gtk_table_new (3, 2, FALSE);
  276.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  277.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  278.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  279.   gtk_container_add (GTK_CONTAINER (frame), table);
  280.   gtk_widget_show (table);
  281.  
  282.   button = gimp_spin_button_new (&w.division_x_adj, p.params.division_x,
  283.                  1.0, p.drawable->width, 1.0, 5.0, 0, 1, 0);
  284.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  285.                  _("X:"), 1.0, 0.5,
  286.                  button, 1, TRUE);
  287.   gtk_signal_connect (GTK_OBJECT (w.division_x_adj), "value_changed",
  288.               GTK_SIGNAL_FUNC (division_x_adj_changed),
  289.               NULL);
  290.  
  291.   button = gimp_spin_button_new (&w.division_y_adj, p.params.division_y,
  292.                  1.0, p.drawable->width, 1.0, 5.0, 0, 1, 0);
  293.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  294.                  _("Y:"), 1.0, 0.5,
  295.                  button, 1, TRUE);
  296.   gtk_signal_connect (GTK_OBJECT (w.division_y_adj), "value_changed",
  297.               GTK_SIGNAL_FUNC (division_y_adj_changed),
  298.               NULL);
  299.  
  300.   button = gimp_spin_button_new (&w.tile_size_adj, p.params.tile_size,
  301.                  1.0, MAX (p.drawable->width,
  302.                        p.drawable->height),
  303.                  1.0, 5.0, 0, 1, 0);
  304.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
  305.                  _("Size:"), 1.0, 0.5,
  306.                  button, 1, TRUE);
  307.   gtk_signal_connect (GTK_OBJECT (w.tile_size_adj), "value_changed",
  308.               GTK_SIGNAL_FUNC (tile_size_adj_changed),
  309.               NULL);
  310.  
  311.   frame = gimp_radio_group_new2 (TRUE, _("Fractional Pixels"),
  312.                  gimp_radio_button_update,
  313.                  &p.params.fractional_type,
  314.                  (gpointer) p.params.fractional_type,
  315.  
  316.                  _("Background"),
  317.                  (gpointer) FRACTIONAL_TYPE_BACKGROUND, NULL,
  318.                  _("Ignore"),
  319.                  (gpointer) FRACTIONAL_TYPE_IGNORE, NULL,
  320.                  _("Force"),
  321.                  (gpointer) FRACTIONAL_TYPE_FORCE, NULL,
  322.  
  323.                  NULL);
  324.   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  325.   gtk_widget_show (frame);
  326.  
  327.   box = GTK_BIN (frame)->child;
  328.  
  329.   sep = gtk_hseparator_new ();
  330.   gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 1);
  331.   gtk_widget_show (sep);
  332.  
  333.   button = gtk_check_button_new_with_label(_("Centering"));
  334.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), p.params.centering);
  335.   gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
  336.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  337.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  338.               &p.params.centering);
  339.   gtk_widget_show (button);
  340.  
  341.   /* Right */
  342.   vbox = gtk_vbox_new (FALSE, 4);
  343.   gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
  344.   gtk_widget_show (vbox);
  345.  
  346.   frame = gtk_frame_new (_("Movement"));
  347.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  348.   gtk_widget_show (frame);
  349.  
  350.   table = gtk_table_new (2, 2, FALSE);
  351.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  352.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  353.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  354.   gtk_container_add (GTK_CONTAINER (frame), table);
  355.   gtk_widget_show (table);
  356.  
  357.   button = gimp_spin_button_new (&adjustment, p.params.move_max_rate,
  358.                  0.0, 100.0, 1.0, 10.0, 0, 1, 0);
  359.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  360.                  _("Max (%):"), 1.0, 0.5,
  361.                  button, 1, TRUE);
  362.   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
  363.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  364.               &p.params.move_max_rate);
  365.  
  366.   button = gtk_check_button_new_with_label (_("Wrap Around"));
  367.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
  368.                 p.params.wrap_around);
  369.   gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 2, 1, 2);
  370.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  371.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  372.               &p.params.wrap_around);
  373.   gtk_widget_show (button);
  374.  
  375.   frame = gimp_radio_group_new2 (TRUE, _("Background Type"),
  376.                  gimp_radio_button_update,
  377.                  &p.params.background_type,
  378.                  (gpointer) p.params.background_type,
  379.  
  380.                  _("Transparent"),
  381.                  (gpointer) BACKGROUND_TYPE_TRANSPARENT, NULL,
  382.                  _("Inverted Image"),
  383.                  (gpointer) BACKGROUND_TYPE_INVERTED, NULL,
  384.                  _("Image"),
  385.                  (gpointer) BACKGROUND_TYPE_IMAGE, NULL,
  386.                  _("Foreground Color"),
  387.                  (gpointer) BACKGROUND_TYPE_FOREGROUND, NULL,
  388.                  _("Background Color"),
  389.                  (gpointer) BACKGROUND_TYPE_BACKGROUND, NULL,
  390.                  (gpointer) 1, /* button without label */
  391.                  (gpointer) BACKGROUND_TYPE_COLOR, &button,
  392.  
  393.                  NULL);
  394.   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  395.   gtk_widget_show (frame);
  396.  
  397.   color_button = gimp_color_button_new (_("Background Color"), 100, 16,
  398.                     p.params.background_color,
  399.                     p.drawable_has_alpha ? 4 : 3);
  400.   gtk_container_add (GTK_CONTAINER (button), color_button);
  401.   gtk_widget_show (color_button);
  402.  
  403.   gtk_widget_set_sensitive (color_button,
  404.                 p.params.background_type == BACKGROUND_TYPE_COLOR);
  405.   gtk_object_set_data (GTK_OBJECT (button), "set_sensitive", color_button);
  406.  
  407.   gtk_widget_show (dialog);
  408.  
  409.   gtk_main ();
  410.   gdk_flush ();
  411. }
  412.  
  413. /*============================================================================*/
  414. /* PLUGIN CORE                                                                */
  415. /*============================================================================*/
  416.  
  417. typedef struct _Tile Tile;
  418. struct _Tile
  419. {
  420.   guint x;
  421.   guint y;
  422.   gint  z;
  423.   guint width;
  424.   guint height;
  425.   gint  move_x;
  426.   gint  move_y;
  427. };
  428.  
  429. static gint
  430. tile_compare (const void *x,
  431.           const void *y)
  432. {
  433.   return ((Tile *) x)->z - ((Tile *) y)->z;
  434. }
  435.  
  436. /*----------------------------------------------------------------------------*/
  437.  
  438. static inline gdouble
  439. drand (void)
  440. {
  441.   static gdouble R = 2.0 / (gdouble) G_MAXRAND;
  442. #ifdef G_OS_WIN32
  443.   return (gdouble) RAND_FUNC () * R - 1.0;
  444. #else
  445.   return (gdouble) rand () * R - 1.0;
  446. #endif
  447. }
  448.  
  449. static inline void
  450. random_move (gint *x,
  451.          gint *y,
  452.          gint  max)
  453. {
  454.   gdouble angle  = drand () * G_PI;
  455.   gdouble radius = drand () * (gdouble) max;
  456.   *x = (gint) (radius * cos (angle));
  457.   *y = (gint) (radius * sin (angle));
  458. }
  459.  
  460. /*----------------------------------------------------------------------------*/
  461.  
  462. static void
  463. overlap_RGB (guchar       *base,
  464.          const guchar *top)
  465. {
  466.   base[0] = top[0];
  467.   base[1] = top[1];
  468.   base[2] = top[2];
  469. }
  470.  
  471. static void
  472. overlap_RGBA (guchar       *base,
  473.           const guchar *top)
  474. {
  475.   gdouble R1 = (gdouble) base[0] / 255.0;
  476.   gdouble G1 = (gdouble) base[1] / 255.0;
  477.   gdouble B1 = (gdouble) base[2] / 255.0;
  478.   gdouble A1 = (gdouble) base[3] / 255.0;
  479.   gdouble R2 = (gdouble)  top[0] / 255.0;
  480.   gdouble G2 = (gdouble)  top[1] / 255.0;
  481.   gdouble B2 = (gdouble)  top[2] / 255.0;
  482.   gdouble A2 = (gdouble)  top[3] / 255.0;
  483.   gdouble A3 = A2 + A1 * (1.0 - A2);
  484.   if(0.0 < A3)
  485.     {
  486.       gdouble R3 = (R1 * A1 * (1.0 - A2) + R2 * A2) / A3;
  487.       gdouble G3 = (G1 * A1 * (1.0 - A2) + G2 * A2) / A3;
  488.       gdouble B3 = (B1 * A1 * (1.0 - A2) + B2 * A2) / A3;
  489.       R3 = CLAMP (R3, 0.0, 1.0);
  490.       G3 = CLAMP (G3, 0.0, 1.0);
  491.       B3 = CLAMP (B3, 0.0, 1.0);
  492.       base[0] = (guchar) (R3 * 255.0);
  493.       base[1] = (guchar) (G3 * 255.0);
  494.       base[2] = (guchar) (B3 * 255.0);
  495.       base[3] = (guchar) (A3 * 255.0);
  496.     }
  497.   else
  498.     {
  499.       base[0] = 0;
  500.       base[1] = 0;
  501.       base[2] = 0;
  502.       base[3] = 0;
  503.     }
  504. }
  505.  
  506. /*----------------------------------------------------------------------------*/
  507.  
  508. static inline void
  509. filter (void)
  510. {
  511.   static void (* overlap)(guchar *, const guchar *);
  512.   GimpPixelRgn  src;
  513.   GimpPixelRgn  dst;
  514.   guchar     pixel[4];
  515.   gint       division_x;
  516.   gint       division_y;
  517.   gint       offset_x;
  518.   gint       offset_y;
  519.   Tile      *tiles;
  520.   gint       numof_tiles;
  521.   Tile      *t;
  522.   gint       i;
  523.   gint       x;
  524.   gint       y;
  525.   gint       move_max_pixels;
  526.   guchar    *row_buffer;
  527.   gint       clear_x0;
  528.   gint       clear_y0;
  529.   gint       clear_x1;
  530.   gint       clear_y1;
  531.   gint       clear_width;
  532.   gint       clear_height;
  533.   guchar    *pixels;
  534.   guchar    *buffer;
  535.   gint       dindex;
  536.   gint       sindex;
  537.   gint       px, py;
  538.  
  539.   /* INITIALIZE */
  540.   gimp_pixel_rgn_init (&src, p.drawable, 0, 0,
  541.                p.drawable->width, p.drawable->height, FALSE, FALSE);
  542.   gimp_pixel_rgn_init (&dst, p.drawable, 0, 0,
  543.                p.drawable->width, p.drawable->height, TRUE, TRUE);
  544.   row_buffer = g_new (guchar, p.drawable->bpp * p.drawable->width);
  545.   pixels = g_new (guchar,
  546.           p.drawable->bpp * p.drawable->width * p.drawable->height);
  547.   buffer = g_new (guchar,
  548.           p.drawable->bpp * p.params.tile_size * p.params.tile_size);
  549.  
  550.   overlap = p.drawable_has_alpha ? overlap_RGBA : overlap_RGB;
  551.  
  552.   gimp_progress_init (_("Paper Tile..."));
  553.  
  554.   gimp_drawable_mask_bounds (p.drawable->id,
  555.                  &p.selection.x0, &p.selection.y0,
  556.                  &p.selection.x1, &p.selection.y1);
  557.   p.selection.width  = p.selection.x1 - p.selection.x0;
  558.   p.selection.height = p.selection.y1 - p.selection.y0;
  559.  
  560.   gimp_tile_cache_ntiles (2 * (p.selection.width / gimp_tile_width () + 1));
  561.  
  562.   /* TILES */
  563. #ifdef G_OS_WIN32
  564.   SRAND_FUNC (0);
  565. #else
  566.   srand (0);
  567. #endif
  568.   division_x = p.params.division_x;
  569.   division_y = p.params.division_y;
  570.   if (p.params.fractional_type == FRACTIONAL_TYPE_FORCE)
  571.     {
  572.       if (0 < p.drawable->width  % p.params.tile_size) division_x++;
  573.       if (0 < p.drawable->height % p.params.tile_size) division_y++;
  574.       if (p.params.centering)
  575.     {
  576.       if (1 < p.drawable->width % p.params.tile_size)
  577.         {
  578.           division_x++;
  579.           offset_x =
  580.         (p.drawable->width % p.params.tile_size) / 2 -
  581.         p.params.tile_size;
  582.         }
  583.       else
  584.         {
  585.           offset_x = 0;
  586.         }
  587.       if (1 < p.drawable->height % p.params.tile_size)
  588.         {
  589.           division_y++;
  590.           offset_y =
  591.         (p.drawable->height % p.params.tile_size) / 2 -
  592.         p.params.tile_size;
  593.         }
  594.       else
  595.         {
  596.           offset_y = 0;
  597.         }
  598.     }
  599.       else
  600.     {
  601.       offset_x = 0;
  602.       offset_y = 0;
  603.     }
  604.     }
  605.   else
  606.     {
  607.       if (p.params.centering)
  608.     {
  609.       offset_x = (p.drawable->width  % p.params.tile_size) / 2;
  610.       offset_y = (p.drawable->height % p.params.tile_size) / 2;
  611.     }
  612.       else
  613.     {
  614.       offset_x = 0;
  615.       offset_y = 0;
  616.     }
  617.     }
  618.   move_max_pixels = p.params.move_max_rate * p.params.tile_size / 100.0;
  619.   numof_tiles = division_x * division_y;
  620.   t = tiles = g_new(Tile, numof_tiles);
  621.   for (y = 0; y < division_y; y++)
  622.     {
  623.       gint srcy = offset_y + p.params.tile_size * y;
  624.       for (x = 0; x < division_x; x++, t++)
  625.     {
  626.       gint srcx = offset_x + p.params.tile_size * x;
  627.       if (srcx < 0)
  628.         {
  629.           t->x     = 0;
  630.           t->width = srcx + p.params.tile_size;
  631.         }
  632.       else if (srcx + p.params.tile_size < p.drawable->width)
  633.         {
  634.           t->x     = srcx;
  635.           t->width = p.params.tile_size;
  636.         }
  637.       else
  638.         {
  639.           t->x     = srcx;
  640.           t->width = p.drawable->width - srcx;
  641.         }
  642.       if (srcy < 0)
  643.         {
  644.           t->y      = 0;
  645.           t->height = srcy + p.params.tile_size;
  646.         }
  647.       else if (srcy + p.params.tile_size < p.drawable->height)
  648.         {
  649.           t->y      = srcy;
  650.           t->height = p.params.tile_size;
  651.         }
  652.       else
  653.         {
  654.           t->y      = srcy;
  655.           t->height = p.drawable->height - srcy;
  656.         }
  657. #ifdef G_OS_WIN32
  658.       t->z = RAND_FUNC ();
  659. #else
  660.       t->z = rand ();
  661. #endif
  662.       random_move (&t->move_x, &t->move_y, move_max_pixels);
  663.     }
  664.     }
  665.   qsort (tiles, numof_tiles, sizeof *tiles, tile_compare);
  666.  
  667.   /* CLEAR PIXELS */
  668.   for (y = 0; y < p.drawable->height; y++)
  669.     {
  670.       gimp_pixel_rgn_get_row (&src,
  671.                   &pixels[p.drawable->bpp*p.drawable->width*y],
  672.                   0, y, p.drawable->width);
  673.     }
  674.   if (p.params.fractional_type == FRACTIONAL_TYPE_IGNORE)
  675.     {
  676.       clear_x0     = offset_x;
  677.       clear_y0     = offset_y;
  678.       clear_width  = p.params.tile_size * division_x;
  679.       clear_height = p.params.tile_size * division_y;
  680.     }
  681.   else
  682.     {
  683.       clear_x0     = 0;
  684.       clear_y0     = 0;
  685.       clear_width  = p.drawable->width;
  686.       clear_height = p.drawable->height;
  687.     }
  688.   clear_x1 = clear_x0 + clear_width;
  689.   clear_y1 = clear_y0 + clear_height;
  690.  
  691.   switch (p.params.background_type)
  692.     {
  693.     case BACKGROUND_TYPE_TRANSPARENT:
  694.       for (y = clear_y0; y < clear_y1; y++)
  695.     {
  696.       for (x = clear_x0; x < clear_x1; x++)
  697.         {
  698.           dindex = p.drawable->bpp * (p.drawable->width * y + x);
  699.           for (i = 0; i < p.drawable->bpp; i++)
  700.         {
  701.           pixels[dindex+i] = 0;
  702.         }
  703.         }
  704.     }
  705.       break;
  706.  
  707.     case BACKGROUND_TYPE_INVERTED:
  708.       for (y = clear_y0; y < clear_y1; y++)
  709.     {
  710.       for (x = clear_x0; x < clear_x1; x++)
  711.         {
  712.           dindex = p.drawable->bpp * (p.drawable->width * y + x);
  713.           pixels[dindex+0] = 255 - pixels[dindex+0];
  714.           pixels[dindex+1] = 255 - pixels[dindex+1];
  715.           pixels[dindex+2] = 255 - pixels[dindex+2];
  716.         }
  717.     }
  718.       break;
  719.  
  720.     case BACKGROUND_TYPE_IMAGE:
  721.       break;
  722.  
  723.     case BACKGROUND_TYPE_FOREGROUND:
  724.       gimp_palette_get_foreground (&pixel[0], &pixel[1], &pixel[2]);
  725.       pixel[3] = 255;
  726.       for (y = clear_y0; y < clear_y1; y++)
  727.     {
  728.       for (x = clear_x0; x < clear_x1; x++)
  729.         {
  730.           dindex = p.drawable->bpp * (p.drawable->width * y + x);
  731.           for (i = 0; i < p.drawable->bpp; i++)
  732.         {
  733.           pixels[dindex+i] = pixel[i];
  734.         }
  735.         }
  736.     }
  737.       break;
  738.  
  739.     case BACKGROUND_TYPE_BACKGROUND:
  740.       gimp_palette_get_background (&pixel[0], &pixel[1], &pixel[2]);
  741.       pixel[3] = 255;
  742.       for (y = clear_y0; y < clear_y1; y++)
  743.     {
  744.       for (x = clear_x0; x < clear_x1; x++)
  745.         {
  746.           dindex = p.drawable->bpp * (p.drawable->width * y + x);
  747.           for(i = 0; i < p.drawable->bpp; i++)
  748.         {
  749.           pixels[dindex+i] = pixel[i];
  750.         }
  751.         }
  752.     }
  753.       break;
  754.  
  755.     case BACKGROUND_TYPE_COLOR:
  756.       pixel[0] = p.params.background_color[0];
  757.       pixel[1] = p.params.background_color[1];
  758.       pixel[2] = p.params.background_color[2];
  759.       pixel[3] = p.params.background_color[3];
  760.       for (y = clear_y0; y < clear_y1; y++)
  761.     {
  762.       for (x = clear_x0; x < clear_x1; x++)
  763.         {
  764.           dindex = p.drawable->bpp * (p.drawable->width * y + x);
  765.           for(i = 0; i < p.drawable->bpp; i++)
  766.         {
  767.           pixels[dindex+i] = pixel[i];
  768.         }
  769.         }
  770.     }
  771.       break;
  772.     }
  773.  
  774.   /* DRAW */
  775.   for (t = tiles, i = 0; i < numof_tiles; i++, t++)
  776.     {
  777.       gint x0 = t->x + t->move_x;
  778.       gint y0 = t->y + t->move_y;
  779.  
  780.       gimp_pixel_rgn_get_rect (&src, buffer, t->x, t->y, t->width, t->height);
  781.  
  782.       for (y = 0; y < t->height; y++)
  783.     {
  784.       py = y0 + y;
  785.       for (x = 0; x < t->width; x++)
  786.         {
  787.           px = x0 + x;
  788.           sindex = p.drawable->bpp * (t->width * y + x);
  789.           if (0 <= px && px < p.drawable->width &&
  790.           0 <= py && py < p.drawable->height)
  791.         {
  792.           dindex = p.drawable->bpp * (p.drawable->width * py + px);
  793.           overlap(&pixels[dindex], &buffer[sindex]);
  794.         }
  795.           else if (p.params.wrap_around)
  796.         {
  797.           px = (px + p.drawable->width)  % p.drawable->width;
  798.           py = (py + p.drawable->height) % p.drawable->height;
  799.           dindex = p.drawable->bpp * (p.drawable->width * py + px);
  800.           overlap(&pixels[dindex], &buffer[sindex]);
  801.         }
  802.         }
  803.     }
  804.  
  805.       gimp_progress_update ((gdouble) i / (gdouble) numof_tiles / 2.0);
  806.     }
  807.  
  808.   /* SEND IT */
  809.   for (y = 0; y < p.drawable->height; y++)
  810.     {
  811.       gimp_pixel_rgn_set_row (&dst, &pixels[p.drawable->bpp*p.drawable->width*y],
  812.                   0, y, p.drawable->width);
  813.       gimp_progress_update ((gdouble) y /
  814.                 (gdouble) p.drawable->height / 2.0 + 0.5);
  815.     }
  816.  
  817.   gimp_drawable_flush (p.drawable);
  818.   gimp_drawable_merge_shadow (p.drawable->id, TRUE);
  819.   gimp_drawable_update (p.drawable->id, p.selection.x0, p.selection.y0,
  820.             p.selection.width, p.selection.height);
  821.  
  822.   /* FREE */
  823.   g_free (buffer);
  824.   g_free (pixels);
  825.   g_free (tiles);
  826.   g_free (row_buffer);
  827. }
  828.  
  829. /*============================================================================*/
  830. /* PLUGIN INTERFACES                                                          */
  831. /*============================================================================*/
  832.  
  833. static void
  834. plugin_query (void)
  835. {
  836.   static GimpParamDef     args[]            =
  837.   {
  838.     { GIMP_PDB_INT32,    "run_mode",         "run mode"                         },
  839.     { GIMP_PDB_IMAGE,    "image",            "input image"                      },
  840.     { GIMP_PDB_DRAWABLE, "drawable",         "input drawable"                   },
  841.     { GIMP_PDB_INT32,    "tile_size",        "tile size (pixels)"               },
  842.     { GIMP_PDB_FLOAT,    "move_max",         "max move rate (%)"                },
  843.     { GIMP_PDB_INT32,    "fractional_type",  "0:Background 1:Ignore 2:Force"    },
  844.     { GIMP_PDB_INT32,    "wrap_around",      "wrap around (bool)"               },
  845.     { GIMP_PDB_INT32,    "centering",        "centering (bool)"                 },
  846.     { GIMP_PDB_INT32,    "background_type",
  847.       "0:Transparent 1:Inverted 2:Image? 3:FG 4:BG 5:Color"                  },
  848.     { GIMP_PDB_INT32,    "background_color", "background color (for bg-type 5)" },
  849.     { GIMP_PDB_INT32,    "background_alpha", "opacity (for bg-type 5)"          }
  850.   };
  851.   static gint numof_args        = sizeof args / sizeof args[0];
  852.  
  853.   gimp_install_procedure (PLUGIN_PROCEDURE_NAME,
  854.               "Cuts an image into paper tiles, and slides each paper tile.",
  855.               "This plug-in cuts an image into paper tiles and slides each paper tile.",
  856.               "Hirotsuna Mizuno <s1041150@u-aizu.ac.jp>",
  857.               "Copyright (c)1997-1999 Hirotsuna Mizuno",
  858.               _("September 31, 1999"),
  859.               N_("<Image>/Filters/Map/Paper Tile..."),
  860.               "RGB*",
  861.               GIMP_PLUGIN,
  862.               numof_args, 0,
  863.               args, NULL);
  864. }
  865.  
  866. static void
  867. plugin_run (gchar      *name,
  868.         gint        numof_params,
  869.         GimpParam  *params,
  870.         gint       *numof_return_vals,
  871.         GimpParam **return_vals)
  872. {
  873.   GimpPDBStatusType status;
  874.  
  875.   INIT_I18N_UI();
  876.  
  877.   status = GIMP_PDB_SUCCESS;
  878.   p.run  = FALSE;
  879.   p.run_mode = params[0].data.d_int32;
  880.   p.image    = params[1].data.d_image;
  881.   p.drawable = gimp_drawable_get (params[2].data.d_drawable);
  882.   p.drawable_has_alpha = gimp_drawable_has_alpha (p.drawable->id);
  883.  
  884.   if (gimp_drawable_is_rgb (p.drawable->id))
  885.     {
  886.       switch (p.run_mode)
  887.     {
  888.     case GIMP_RUN_INTERACTIVE:
  889.       params_load_from_gimp ();
  890.       open_dialog ();
  891.       break;
  892.  
  893.     case GIMP_RUN_NONINTERACTIVE:
  894.       if (numof_params == 11)
  895.         {
  896.           p.params.tile_size  = params[3].data.d_int32;
  897.           p.params.division_x = p.drawable->width  / p.params.tile_size;
  898.           p.params.division_y = p.drawable->height / p.params.tile_size;
  899.           p.params.move_max_rate = params[4].data.d_float;
  900.           p.params.fractional_type = (FractionalType)params[5].data.d_int32;
  901.           p.params.wrap_around = params[6].data.d_int32;
  902.           p.params.centering = params[7].data.d_int32;
  903.           p.params.background_type = (BackgroundType)params[8].data.d_int32;
  904.           p.params.background_color[0] = params[9].data.d_color.red;
  905.           p.params.background_color[1] = params[9].data.d_color.green;
  906.           p.params.background_color[2] = params[9].data.d_color.blue;
  907.           p.params.background_color[3] = params[10].data.d_int32;
  908.           p.run = TRUE;
  909.         }
  910.       else
  911.         {
  912.           status = GIMP_PDB_CALLING_ERROR;
  913.         }
  914.       break;
  915.  
  916.     case GIMP_RUN_WITH_LAST_VALS:
  917.       params_load_from_gimp ();
  918.       p.run = TRUE;
  919.       break;
  920.     }
  921.     }
  922.   else
  923.     {
  924.       status = GIMP_PDB_EXECUTION_ERROR;
  925.     }
  926.  
  927.   if (status == GIMP_PDB_SUCCESS && p.run)
  928.     {
  929.       params_save_to_gimp ();
  930.  
  931.       filter ();
  932.  
  933.       if (p.run_mode != GIMP_RUN_NONINTERACTIVE)
  934.         gimp_displays_flush ();
  935.     }
  936.  
  937.   gimp_drawable_detach (p.drawable);
  938.   
  939.   {
  940.     static GimpParam return_value[1];
  941.     return_value[0].type          = GIMP_PDB_STATUS;
  942.     return_value[0].data.d_status = status;
  943.     *numof_return_vals            = 1;
  944.     *return_vals                  = return_value;
  945.   }
  946. }
  947.  
  948. GimpPlugInInfo PLUG_IN_INFO =
  949. {
  950.   NULL,
  951.   NULL,
  952.   plugin_query,
  953.   plugin_run
  954. };
  955.  
  956. MAIN ()
  957.